< go back

Playing around with an ssg

June 24, 2026 - site

So I really wanted to create an art gallery and was really inspired by the one stupied has on their site! (seriously, check out their blog posts about how they their art gallery and oc repo-- it's super cool!)

This led me to finally try using an ssg for my site, since I wanted to be able to easily add art pieces and have an easy to navigate tagging system!

I followed this tutorial by flamed fury to set up 11ty on my site and transfer my stuff over!

Note: This is not a tutorial but I just thought I'd keep something here for future reference (it's also kind of written like a tutorial bc it's what felt the most natural to me). I'm also very technologically illiterate and have no idea what I'm doing so things here might be wrong or poorly done!!!

So, if you're trying to set up an ssg yourself, hop on over to flamed fury's tutorial instead lol (a lot of the setup written here is the exact same anyways, since I followed their tutorial for it).

I just find that sometimes, it helps me to read about how other people set up certain things to try and cobble together something for myself that works. So maybe this will be useful to someone out there. idk.

the setup

I downloaded nodejs through homebrew and made sure it was downloaded correctly

brew install node@24
node -v && npm -v

Then I ran the following commands within my existing neocities folder to set up 11ty :D

npm init -y
npm install @11ty/eleventy

Now, add this to package.json:

  "scripts": {
    "start": "npx @11ty/eleventy --serve",
    "build": "npx @11ty/eleventy"
  },

This basically lets us build the site when we run the npm start command in terminal!

Time for some config. In the project root (alongside package.json and package-lock.json), create a file called .eleventy.config.js and add this:

module.exports = function (eleventyConfig) {
  return {
    dir: {
      input: "src",
      output: "public-build",
      includes: "_includes",
    },
  };
};

This basically tells 11ty to use files stored inside the "src" folder and build the site inside the "public-build" folder. Sooo basically, the src folder will store all your working files (eg. if you already have an existing site, all those files should be moved to src). Public-build is what the browser sees. When you run npm start (to build the site), 11ty will go through all the files you have in the src folder and spit out new files in the public-build folder. The browser will then be displaying files inside public-build.

That being said, let's create a src folder and move all of the pre-existing files from the site into it!

Here's what the file structure should look like!

root folder/
|-- package-lock.json
|-- package.json   
|-- node_modules/
|   |-- bunch
|   |-- of
|   |-- stuff
|   `-- ...
|-- src/
|   |-- everything else
|   |-- that was already
|   |-- in my site
|   |-- like index.html
|   `-- ...

I'm using github to push my site directly to neocities, which I'll go over later. For now, add these to your .gitignore file since we don't want to push files within these folders into github (or create it in the root folder).

node_modules
public-build

Time to test! I ran npm start in terminal from inside the root directory (I actually have vscode open in another parent folder so I had to cd into my site first).

The terminal should output a link that looks like this "http://localhost:8080". Go to that link and you should see your site!! Now, everytime you change a file inside the src folder, it will automatically update with changes (really quickly too)! Very cool :D

Side note: if your site stops updating, it might be because there's an error in your code. In the terminal, instead of a green message saying "copied __ wrote __ files in __ seconds", there'll be a read message saying "wrote 0 files in __ seconds" :( If you scroll up, usually under "Problem writing eleventy templates", it will tell you exactly where the issue is and/or what you need to fix. Troubleshoot from there.

Fixing my file structure

Here is where we start diverging a little from the tutorial I was following.

My existing site's file structure was set up like this, with each page (except index.html) in its own folder:

src/
|-- index.html
|-- homepage/
|   |-- assets for index.html/
|   `-- more assets for index.html/
|-- about/
|   `-- assets for about.html/
|-- links/
|   `-- assets for links.html/
`-- etc .../

This gave me several problems. Firstly, all the images on my homepage were missing :( and all my links were broken!

Let's tackle the images first. I have all my assets in subfolders within src. However, 11ty won't actually copy these files into public-build (the folder the browser reads) unless we specifically tell it to.

I updated by eleventy.js with a PassthroughCopy for every folder I needed it to go through so that it looks something like this:

PassthroughCopymodule.exports = function (eleventyConfig) {
  eleventyConfig.addPassthroughCopy("./src/fonts");
  eleventyConfig.addPassthroughCopy("./src/img");
  eleventyConfig.addPassthroughCopy("./src/css");
  eleventyConfig.addPassthroughCopy("./src/scripts");

  eleventyConfig.addPassthroughCopy("./src/homepage");
  eleventyConfig.addPassthroughCopy("./src/about");
  eleventyConfig.addPassthroughCopy("./src/links");

  return {
    passthroughFileCopy: true,
    dir: {
      input: "src",
      output: "public",
      includes: "_includes",
    },
  };
};

Obviously, it would have been better to keep everything in a few assets folders and not have an individual folder per page, but I already had my site set up like this and didn't really feel like changing it for the time being.

I also moved all the html files into the src folder so 11ty processes them correctly (I think). So the page folders (eg. src/about, src/links, etc) only contained assets. The actual about.html or links.html files are just in the src folder.

Since they were moved, I needed to update the image links. I just used find and replace on each file to add about/ or links/ etc at the start of the link.

As for the page links, when processing an html file, 11ty creates a new folder with the page name and places the html file as "index.html" within that folder. So for example, about.html would become /about/index.html. To override this, I just needed to add a permalink property to each page.

By placing this on the top of a page, you can add YAML properties:

---
property1: value
property2: value
---

I added this to the top of each file. Eg:

---
title: about
layout: base.njk
permalink: about.html
---

Yay! Links are fixed!

Templates

The cool part about using an SSG is that you can use template layouts instead of typing out the same thing everywhere!

Create a folder called _includes/ in src and create a file called base.njk inside of it. We want to keep all the html boilerplate stuff inside of this file instead of having it in each individual file.

This is what my base.njk looks like (I just copy and pasted it from an existing file and deleted the actual content):

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" type="text/css" href="/css/fonts.css">
    <link rel="stylesheet" type="text/css" href="/css/scanlines.css">
    <link rel="stylesheet" href="/css/windows.css">
    <script type="text/javascript" src="/scripts/tooltipfollow.js"></script>

    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>SLEEPY SHROOM ☆</title>
    <link rel="icon" type="image/x-icon" href="/homepage/pixels/mikuSHOCK.png">

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

    <script src="https://kit.fontawesome.com/6c37cd7ad3.js" crossorigin="anonymous"></script>
</head>
{{ content | safe }}
</html>

Probably bad practice, but I linked all the css snippets and scripts here so they're in all the files I create.

The {{ content | safe }} tells 11ty that for any page that uses this template to put the page content in that spot.

I went ahead and deleted all this boilerplate stuff from my existing files so they all look something like this (Probably also bad practice, but I like having my CSS in the same file as my HTMl so I can edit them together more easily):

---
title: about
layout: base.njk
permalink: about.html
---
<style>
    .all{
        my: css;
        stays: here;
    }
</style>
<body>
    my page content
</body>

Everything should be functioning correctly now! Just like it was before :D

Bonus: setting up my links page

One thing I used cool ssg powers for was in my links page. I wanted to be able to update it without having to manually copy-paste the html bits each time I wanted to add a new button.

To do this, I created a folder called _data/ in src and added a file called links.json.

I added all the buttons I had into the file in this sorta format:

[
 {
    "name": "zhongvie",
    "link": "https://zhongvie.neocities.org/",
    "button-img": "img/buttons/aid.gif",
    "desc": "Such an extensive site themed around her oc sylvie! I've seen her on a bunch of small indie-esque social media sites and she's got lots of cool thoughts and art"
 },
 {
    "name": "electric tenshi",
    "link": "https://electric-tenshi.nekoweb.org/",
    "button-img": "https://file.garden/Z3nf4eZzZUtRPfhK/stuff/wvdtkw.gif",
    "desc": "Miku themed site! Site design is super cool and there's SO MUCH CONTENT!! Really huge inspiration for me"
 },
]

Now, going back to my links.html page, I updated my table to get the properties from this json file. It loops through each entry in links.json and adds a new table row with its properties.

<table>
    {% for site in links %}
    <tr>
        <td><a href="{{ site.link }}"><img src="{{ site.button-img }}" alt=""></a></td>
        <td>{{ site.desc }}</td>
    </tr>
    {% endfor %}
</table>

Tbh idk if its really that much faster to add to the json file than to just do it directly in html but I guess it's a bit cleaner to look at.

short interlude: I'm dumb

Right! Now that the site is up and running, we need to find a way to send it over to neocities. I used github to do this.

I actually already had a github repo for my site AND a deploy to neocities action that I used before, so it should have pretty easy to get this up and running, right?

Unfortunately I'm dumb and had forgotten essentially everything I learned the last time I used this github repo (for some reason I opted to stop using it and manually drop my files into neocities instead?? idk I don't remember why).

This caused MANY problems, once again due to my lack of braincells. Basically, I decided to move my git files and folders out of my site folder and into its parent (since I have a couple other folders in here that I want tracked). It's probably better practice to just separate them into different repos but I'm lazy and with this I can work on both at the same time without switching folders on vscode.

So it looks something like this:

root folder/
|-- public/
|   |-- src/
|   |-- public-build/
|   |   other 11ty and nodejs stuff
|-- old site/
|-- other folder I want tracked/

Long story short, I forgot to add those other folders to be tracked and decided to override everything with my last commit. That last commit wa from INSIDE the public/ folder, so when I did that, everything in my folder got replaced by what was inside public/.

UM NOT FUN

I managed to recover some stuff from old backups, but a lot of my old site's assets are gone :(

Oh, and fun fact for anyone who uses vscode and doesn't know this: vscode actually saves history for all your files!! If you go into your command palette (cmd+P or ctrl+p then type >) and look for local history: find entry to restore, you can recover a bunch of old files that you've edited even if they're deleted!! THANK YOU VSCODE

connecting to github

OK NOW WE'RE BACK ON TRACK

I'm just going to keep this bit of connecting to github here as future reference for myself. Again, if you're trying to do this, go find an actual tutorial by someone who knows what they're doing lol. Things I say here or terminology I use might (probably) be wrong.

First! Go onto github.com and make a new repository for your site.

In your terminal, use cd "path/to/folder" to go into the folder you want git in (cd stands for change directory). Then do git init to initialise git. You should see a .github/ folder there now (on mac, if you can't see it, use cmd+shift+. to show hidden files).

Then git remote add origin name where name is the link to your repo on github (it should show up under the instructions github gives you). This connects your folder to the repo on github.

Do git remote -v to check that they're connected. It should output something like this:

origin link (fetch)
origin link (push)

We now want to add files to be tracked by git. If you don't add a file to git, they will be there locally on your device but won't be tracked and won't show up on github.

First off, if you're on mac, we want to make sure .DS_Store doesn't get tracked. Each folder on mac has this pesky little file that we don't want added to our site when we deploy it to neocities.

Go to your .gitignore file (or create one in the root directory if you don't have one) and add .DS_Store into it. This file basically just tells git not to track certain files. Here's what mine looks like:

.DS_Store
node_modules
public-build

I already had my git set up before this, so I used this command to remove existing .DS_store from being tracked (it stays locally but removes it from the list of files that git is tracking):

find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch

Ok, now we can add files. Run git add . to add everything in the root folder. Then git ls-files to get a list of all tracked files to check if everything's there.

Then git commit -m "commit message" to commit your changes. Commits are like snapshots or savepoints for your repo.

Run git push origin master to push changes! This takes the snapshot of all your files from the last commit your made, and sends it over to your remote repo. On github, you should now see all your files!! (If it didn't work, double check that the branch - shown on the top left - is called "master". If not, replace "master" with whatever it's called - might be "main")

deploy to neocities

Ok, time to make it send things over to neocities.

Here is a cool tutorial on how to do it, and this is the github action I used.

Go to your .github/ folder (again, if you're on mac and can't see it, do cmd+shift+. or just go into it via vscode or terminal) and add a workflows/ folder. From there, add a file called neocities.yml. Paste in the code found in this action.

Since my site is in a subfolder of my repo, I added this after the runs-on: ubuntu-latest line to tell it to build the site from my public/ folder (where all the 11ty and nodejs files are):

defaults:
      run:
        working-directory: ./public  # All steps run here

This is what my files looks like:

name: Deploy to neocities

# only run on changes to main. Use main or master depending on whatever your default branch is called.
on:
  push:
    branches:
      - master

concurrency: # prevent concurrent deploys doing strange things
  group: deploy-to-neocities
  cancel-in-progress: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./public  # All steps run here
    steps:
    # The checkout step copies your repo into the action runner. Important!
    - uses: actions/checkout@v4
    # Set up any tools and build steps here
    # This example uses a Node.js toolchain to build a site
    # If you don't need Node.js to build your site, you can omit this.
    - name: Use Node.js
      uses: actions/setup-node@v4
      with:
        node-version: lts/*
    # If you have a different build process, replace this with your own build steps
    - name: Install deps and build
      run: |
        npm i
        npm run build
    # When the dist_dir is ready, deploy it to neocities
    # Here we deploy the folder named `public`
    - name: Deploy to neocities
      uses: bcomnes/deploy-to-neocities@v3
      with:
        api_key: $
        cleanup: false
        neocities_supporter: false # set this to true if you have a supporter account and want to bypass unsuported files filter.
        preview_before_deploy: true # print a deployment plan prior to waiting for files to upload.
        dist_dir: public/public-build

dist_dir should be the same name as the output folder you specified in your .eleventy.config.js file.

Go to https://neocities.org/settings/#api_key and copy the api key shown there. Then on github, go to settings (of your repo) and create a secret. Title it NEOCITIES_API_TOKEN and the value as the api key you copied.

Everything should work now. To test it, change something on your site, commit it, and push it (if you use vscode you can do this straight on the editor if you switch to source control on the left)!

On github, on top of your repo files, you should see text with the name of your commit and a little orange dot. If you click on the dot, a pop-up should show up with your deploy to neocities action. If it turns into a green tick, it worked!! Otherwise, if you click details, you can see exactly where the action failed and troubleshoot from there.

Few problems I ran into:

on:
  push:
    branches:
      - master

Anyways, if all went smoothly, your site should have gotten the update you made on neocities!! YAYYY

cLosing notes

Woahhh closing notes. How fancy!! lol. Anyways, this has been quite the endeavor lol. Should NOT have been that difficult had I set up a new git repo instead of messing around with an existing one. Also I'm stupid and had it inside of an icloud folder so ran into a bunch of performance issues with that (cough cough fileproviderd taking up 432098% of my cpu)

It was so worth it tho!! It's been lots of fun playing around with 11ty and trying to make this this page and my art gallery! Really cool that I can just write in md and have it spit out a pretty page for me :D